from typing import Iterable
import functools
import warnings


class Field:
    def __init__(self, name, required=True):
        self.name = name
        self.required = required
        self.content = None

    def __set__(self, instance, value):
        if type(self) is not Field:
            warnings.warn("Field子对象最好不要赋值")
        self.content = value

    def __get__(self, instance, owner):
        if type(self) is not Field:
            return self
        return self.content

    def _iter_self(self, section: "Field", data: dict):
        fields = (field for field in section.__class__.__dict__.values() if isinstance(field, Field))
        for field in fields:
            if type(field) is Field:
                data[field.name] = field.content
            else:
                data[field.name] = {}
                self._iter_self(field, data[field.name])

    def values(self):
        data = {}
        self._iter_self(self, data)
        return data


class FieldMeta(type):
    base_field = "BaseField"

    # 解析父类 base
    @classmethod
    def parse_base(mcs, bases: tuple, attrs: dict):
        if bases:
            for base in reversed(bases):
                for name, field in base.__dict__.items():
                    if isinstance(field, Field):
                        attrs[name] = field
                mro = base.mro()
                mro.remove(object)
                mro.remove(base)
                mcs.parse_base(mro, attrs)

    def __new__(mcs, klass, bases, attrs):
        custom_attr = {}
        for name, attr in attrs.items():
            if isinstance(attr, Field):
                # custom_attr[f"_{name}"] = attr
                custom_attr[name] = attr
            else:
                custom_attr[name] = attr
        base_attrs = {}
        mcs.parse_base(bases, base_attrs)
        base_attrs.update(custom_attr)  # 更新父类字段属性
        return super().__new__(mcs, klass, bases, base_attrs)

    def __init__(cls, klass, bases, attrs: dict):
        cls.reload = functools.partial(cls.load_attr, cls.klass_fields(), )
        super().__init__(klass, bases, attrs)

    def __call__(cls, case: dict):
        if cls.__name__ == cls.base_field:
            raise Exception(f"{cls.base_field}只应该被继承")

        cls.load_attr(cls.klass_fields(), case, )
        return super().__call__(case)

    def klass_fields(cls, other=None):
        return (field for field in (other or cls).__dict__.values() if isinstance(field, Field))

    def load_attr(cls, fields: Iterable[Field], data: dict, section_name="root"):
        for field in fields:
            val = data.get(field.name)
            if not val:
                if field.required:
                    raise AttributeError(f"当前{section_name}节点缺少{field.name}属性")
                else:
                    field.content = ""
            else:
                if type(field) is not Field:
                    fields = cls.klass_fields(other=field.__class__)
                    cls.load_attr(fields, val, section_name=field.name)
                else:
                    field.content = val


class BaseField(Field, metaclass=FieldMeta):
    dir = Field(name="dir", required=False)
    story = Field(name="story")
    version = Field(name="version")
    title = Field(name="title")
    description = Field(name="description")
    skip = Field(name="skip", required=False)  # 是否 skip

    def __init__(self, case: dict):
        super().__init__(name="test_case")
        self.case = case
